home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 2.toast / pc / sample code / quicktime / quicktimeintro / play movie w.controller / common files / qtutilities.c < prev    next >
Encoding:
Text File  |  2000-10-06  |  52.6 KB  |  1,819 lines

  1. //////////
  2. //
  3. //    File:        QTUtilities.c
  4. //
  5. //    Contains:    Some utilities for working with QuickTime movies.
  6. //                All utilities start with the prefix "QTUtils_".
  7. //
  8. //    Written by:    Tim Monroe
  9. //                Based on the DTSQTUtilities package by Apple DTS.
  10. //                This began as essentially a subset of that package, revised for cross-platform use.
  11. //
  12. //    Copyright:    © 1996-1999 by Apple Computer, Inc., all rights reserved.
  13. //
  14. //    Change History (most recent first):
  15. //
  16. //       <33>         03/08/00    rtm        removed QTUtils_SaveMovie and QTUtils_PrintMoviePICT
  17. //       <32>         03/02/00    rtm        added SelectNoneMovie
  18. //       <31>         01/10/00    rtm        tweaked QTUtils_IsQuickTimeInstalled
  19. //       <30>         12/20/99    rtm        reworked QTUtils_IsMovieFile, to first test for files of type kQTFileTypeMovie
  20. //       <29>         12/09/99    rtm        added QTUtils_PutControllerBarOnTop
  21. //       <28>         12/08/99    rtm        reworked controller button functions to support custom buttom (its mcFlags constant
  22. //                                    is a "Use" constant and not a "Suppress" constant)
  23. //       <27>         11/26/99    rtm        added endian adjustments to _GetMovieFileLoopingInfo and _GetWindowPositionFromFile
  24. //       <26>         11/17/99    rtm        added QTUtils_GetWindowPositionFromFile
  25. //       <25>         05/19/99    rtm        removed QTUtils_GetMovie
  26. //       <24>         05/10/99    rtm        added QTUtils_UpdateMovieVolumeSetting
  27. //       <23>         03/22/99    rtm        updated connection speed code to use constants/data types now in Movies.h
  28. //       <22>         03/11/99    rtm        moved _GetControllerType and _SetControllerType from QTVRUtilities to here;
  29. //                                    added QTUtils_ChangeControllerType
  30. //       <21>         02/03/99    rtm        moved non-QTVR-specific utilities from QTVRUtilities to here
  31. //       <20>         01/26/99    rtm        added QTUtils_ConvertCToPascalString; removed "\p" from any constant strings
  32. //       <19>         01/25/99    rtm        #define'd away QTUtils_GetUsersContentRating and siblings, since content rating
  33. //                                    capability is not in latest feature set
  34. //       <18>         12/09/98    rtm        added QTUtils_GetUsersContentRating
  35. //       <17>         11/18/98    rtm        added QTUtils_GetFrameCount
  36. //       <16>         10/27/98    rtm        added QTUtils_MakeMovieLoop
  37. //       <15>         09/14/98    rtm        added QTUtils_GetUsersConnectionSpeed and QTUtils_SetUsersConnectionSpeed
  38. //       <14>         06/24/98    rtm        added QTUtils_GetMaxWindowDepth and QTUtils_GetMaxScreenDepth
  39. //       <13>         06/04/98    rtm        added QTUtils_DeleteAllReferencesToTrack
  40. //       <12>         05/28/98    rtm        added some typecasting to minimize MSDev compiler warnings
  41. //       <11>         05/19/98    rtm        added QTUtils_MovieHasTimeCodeTrack
  42. //       <10>         05/04/98    rtm        added QTUtils_GetTrackName, QTUtils_SetTrackName, QTUtils_MakeTrackNameByType,
  43. //                                    QTUtils_IsImageFile, and QTUtils_IsMovieFile
  44. //       <9>         02/28/98    rtm        fixed QTUtils_GetMovieFileLoopingInfo and the like
  45. //       <8>         01/14/98    rtm        added QTUtils_ConvertFloatToBigEndian
  46. //       <7>         12/19/97    rtm        added QTUtils_AddUserDataTextToMovie and associated routines;
  47. //                                    added QTUtils_GetMovieFileLoopingInfo and the like
  48. //       <6>         11/06/97    rtm        added QTUtils_MakeSampleDescription
  49. //       <5>         10/29/97    rtm        modified QTUtils_IsMediaTypeInMovie and similar routines to use GetMovieIndTrackType
  50. //       <4>         10/27/97    rtm        added QTUtils_HasQuickTimeVideoEffects
  51. //       <3>         10/17/97    rtm        added QTUtils_MovieHasSoundTrack
  52. //       <2>         09/23/97    rtm        added endian adjustment to QTUtils_PrintMoviePICT
  53. //       <1>         09/10/97    rtm        first file
  54. //       
  55. //////////
  56.  
  57. //////////
  58. //
  59. // header files
  60. //
  61. //////////
  62.  
  63. #ifndef __QTUtilities__
  64. #include "QTUtilities.h"
  65.  
  66.  
  67. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  68. //
  69. // General utilities.
  70. //
  71. // Use these functions to get information about the availability/features of QuickTime or other services.
  72. //
  73. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  74.  
  75. //////////
  76. //
  77. // QTUtils_TrapAvailable
  78. // Check to see whether a given trap is implemented. This is based on IM: Operating System Utilities (p. 8-22).
  79. //
  80. //////////
  81.  
  82. #if TARGET_OS_MAC && !TARGET_API_MAC_CARBON
  83. Boolean QTUtils_TrapAvailable (short theTrapWord)
  84. {
  85.     TrapType        myTrapType;
  86.     short            myNumToolboxTraps;
  87.     
  88.     // determine whether this is a Toolbox or an Operating System trap
  89.     if ((theTrapWord & 0x0800) > 0)
  90.         myTrapType = ToolTrap;
  91.     else
  92.         myTrapType = OSTrap;
  93.  
  94.     if (myTrapType == ToolTrap) {
  95.         theTrapWord = theTrapWord & 0x07FF;
  96.         
  97.         if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap))
  98.             myNumToolboxTraps = 0x0200;
  99.         else
  100.             myNumToolboxTraps = 0x0400;
  101.             
  102.         if (theTrapWord >= myNumToolboxTraps)
  103.             theTrapWord = _Unimplemented;
  104.     }
  105.  
  106.     return(NGetTrapAddress(theTrapWord, myTrapType) != NGetTrapAddress(_Unimplemented, ToolTrap));
  107. }
  108. #endif
  109.  
  110.  
  111. //////////
  112. //
  113. // QTUtils_IsQuickTimeInstalled
  114. // Is QuickTime installed?
  115. //
  116. //////////
  117.  
  118. Boolean QTUtils_IsQuickTimeInstalled (void) 
  119. {
  120.     long        myAttrs;
  121.     OSErr         myErr = noErr;
  122.  
  123.     myErr = Gestalt(gestaltQuickTime, &myAttrs);
  124.  
  125.     return(myErr == noErr);
  126. }
  127.  
  128.  
  129. //////////
  130. //
  131. // QTUtils_IsQuickTimeCFMInstalled
  132. // Are the QuickTime CFM libraries installed?
  133. //
  134. //////////
  135.  
  136. #if TARGET_CPU_PPC
  137. Boolean QTUtils_IsQuickTimeCFMInstalled (void) 
  138. {
  139.     Boolean     myQTCFMAvail = false;
  140.     long        myAttrs;
  141.     OSErr         myErr = noErr;
  142.  
  143.     // test whether the PowerPC QuickTime glue library is present
  144.     myErr = Gestalt(gestaltQuickTimeFeatures, &myAttrs);
  145.     if (myErr == noErr)
  146.         if (myAttrs & (1L << gestaltPPCQuickTimeLibPresent))
  147.             myQTCFMAvail = true;
  148.  
  149.     // test whether a function is available (the library is not moved from the Extension folder);
  150.     // this is the trick to be used when testing if a function is available via CFM
  151.     if (!CompressImage)
  152.         myQTCFMAvail = false;     
  153.  
  154.     return(myQTCFMAvail);
  155. }
  156. #endif
  157.  
  158.  
  159. //////////
  160. //
  161. // QTUtils_GetQTVersion
  162. // Get the version of QuickTime installed.
  163. // The high-order word of the returned long integer contains the version number,
  164. // so you can test a version like this:
  165. //
  166. //        if (((QTUtils_GetQTVersion() >> 16) & 0xffff) >= 0x0210)        // we require QT 2.1 or greater
  167. //            return;
  168. //
  169. //////////
  170.  
  171. long QTUtils_GetQTVersion (void) 
  172. {
  173.     long         myVersion = 0L;
  174.     OSErr         myErr = noErr;
  175.  
  176.     myErr = Gestalt(gestaltQuickTime, &myVersion);
  177.     if (myErr == noErr)
  178.         return(myVersion);
  179.     else
  180.         return(0L);
  181. }
  182.  
  183.  
  184. //////////
  185. //
  186. // QTUtils_HasQuickTimeVideoEffects
  187. // Does the installed version of QuickTime support video effects?
  188. //
  189. // Note: this is a pretty cheesy way of determining whether video effects are available.
  190. //
  191. //////////
  192.  
  193. Boolean QTUtils_HasQuickTimeVideoEffects (void) 
  194. {
  195.     return(((QTUtils_GetQTVersion() >> 16) & 0xffff) >= kQTVideoEffectsMinVers);
  196. }
  197.  
  198.  
  199. //////////
  200. //
  201. // QTUtils_HasFullScreenSupport
  202. // Does the installed version of QuickTime support the full-screen routines?
  203. //
  204. // Note: this is a pretty cheesy way of determining whether full-screen routines are available.
  205. //
  206. //////////
  207.  
  208. Boolean QTUtils_HasFullScreenSupport (void) 
  209. {
  210.     return(((QTUtils_GetQTVersion() >> 16) & 0xffff) >= kQTFullScreenMinVers);
  211. }
  212.  
  213.  
  214. //////////
  215. //
  216. // QTUtils_HasWiredSprites
  217. // Does the installed version of QuickTime support wired sprites?
  218. //
  219. // Note: this is a pretty cheesy way of determining whether wired sprites are available.
  220. //
  221. //////////
  222.  
  223. Boolean QTUtils_HasWiredSprites (void) 
  224. {
  225.     return(((QTUtils_GetQTVersion() >> 16) & 0xffff) >= kQTWiredSpritesMinVers);
  226. }
  227.  
  228.  
  229. //////////
  230. //
  231. // QTUtils_IsQTVRMovie
  232. // Is the specified movie a QTVR movie?
  233. //
  234. // WARNING: This function is intended for use ONLY when you want to determine if you've got a QTVR movie
  235. // but you don't want to use the QuickTime VR API (perhaps QTVR isn't installed...). The preferred way to
  236. // determine if a movie is a QTVR movie is to call QTVRGetQTVRTrack and then QTVRGetQTVRInstance; if you
  237. // get back a non-NULL instance, you've got a QTVR movie.
  238. //
  239. //////////
  240.  
  241. Boolean QTUtils_IsQTVRMovie (Movie theMovie) 
  242. {
  243.     Boolean            myIsQTVRMovie = false;
  244.     OSType            myType;
  245.     
  246.     // QTVR movies have a special piece of user data identifying the movie controller type
  247.     myType = QTUtils_GetControllerType(theMovie);
  248.     
  249.     if ((myType == kQTVRQTVRType) || (myType == kQTVROldPanoType) || (myType == kQTVROldObjectType))
  250.         myIsQTVRMovie = true; 
  251.         
  252.     return(myIsQTVRMovie);
  253. }
  254.  
  255.  
  256. //////////
  257. //
  258. // QTUtils_IsStreamedMovie
  259. // Is the specified movie a streamed movie?
  260. //
  261. //////////
  262.  
  263. Boolean QTUtils_IsStreamedMovie (Movie theMovie) 
  264. {
  265.     return(GetMovieIndTrackType(theMovie, 1, kQTSStreamMediaType, movieTrackMediaType | movieTrackEnabledOnly) != NULL);
  266. }
  267.  
  268.  
  269. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  270. //
  271. // Controller bar utilities.
  272. //
  273. // Use these functions to manipulate the controller bar, its buttons, and the help text displayed in it.
  274. //
  275. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  276.  
  277. //////////
  278. //
  279. // QTUtils_IsControllerBarVisible
  280. // Is the controller bar currently visible?
  281. //
  282. //////////
  283.  
  284. Boolean QTUtils_IsControllerBarVisible (MovieController theMC) 
  285. {
  286.     return((Boolean)MCGetVisible(theMC));
  287. }
  288.  
  289.  
  290. //////////
  291. //
  292. // QTUtils_GetControllerBarHeight
  293. // Return the height of the controller bar displayed by the movie controller.
  294. //
  295. // Note that MCGetControllerBoundsRect returns the rectangle of bar and movie, if attached;
  296. // so we need to detach the controller bar first.
  297. //
  298. //////////
  299.  
  300. short QTUtils_GetControllerBarHeight (MovieController theMC) 
  301. {
  302.     Boolean        wasAttached = false;
  303.     Rect        myRect;
  304.     
  305.     // if the controller bar is attached, detach it (and remember we did so)
  306.     if (MCIsControllerAttached(theMC) == 1) {
  307.         wasAttached = true;
  308.         MCSetControllerAttached(theMC, false);
  309.     }
  310.     
  311.     // get the rectangle of the controller
  312.     MCGetControllerBoundsRect(theMC, &myRect);
  313.     
  314.     // now reattach the controller bar, if it was originally attached
  315.     if (wasAttached)
  316.         MCSetControllerAttached(theMC, true);
  317.     
  318.     return(myRect.bottom - myRect.top);
  319. }
  320.  
  321.  
  322. //////////
  323. //
  324. // QTUtils_HideControllerBar
  325. // Hide the controller bar provided by the movie controller.
  326. //
  327. //////////
  328.  
  329. void QTUtils_HideControllerBar (MovieController theMC) 
  330. {
  331.     MCSetVisible(theMC, false);
  332. }
  333.  
  334.  
  335. //////////
  336. //
  337. // QTUtils_ShowControllerBar
  338. // Show the controller bar provided by the movie controller.
  339. //
  340. //////////
  341.  
  342. void QTUtils_ShowControllerBar (MovieController theMC) 
  343. {
  344.     MCSetVisible(theMC, true);
  345. }
  346.  
  347.  
  348. //////////
  349. //
  350. // QTUtils_ToggleControllerBar
  351. // Toggle the state of the controller bar provided by the movie controller.
  352. //
  353. //////////
  354.  
  355. void QTUtils_ToggleControllerBar (MovieController theMC) 
  356. {
  357.     if (QTUtils_IsControllerBarVisible(theMC))
  358.         QTUtils_HideControllerBar(theMC);
  359.     else
  360.         QTUtils_ShowControllerBar(theMC);
  361. }
  362.  
  363.  
  364. //////////
  365. //
  366. // QTUtils_PutControllerBarOnTop
  367. // Put the movie controller bar at the top of the movie window.
  368. //
  369. //////////
  370.  
  371. void QTUtils_PutControllerBarOnTop (MovieController theMC) 
  372. {
  373.     if (theMC == NULL)
  374.         return;
  375.         
  376.     if (MCIsControllerAttached(theMC) == 1) {
  377.         Rect        myMCRect;
  378.         Rect        myMovieRect;
  379.                         
  380.         MCGetControllerBoundsRect(theMC, &myMCRect);
  381.         myMovieRect = myMCRect;
  382.         myMCRect.bottom = myMCRect.top + QTUtils_GetControllerBarHeight(theMC);
  383.         myMovieRect.top = myMCRect.bottom + 1;
  384.         
  385.         MCSetControllerAttached(theMC, false);
  386.         
  387.         MCPositionController(theMC, &myMovieRect, &myMCRect, 0L);
  388.     }
  389. }
  390.  
  391.  
  392. //////////
  393. //
  394. // QTUtils_HideControllerButton
  395. // Hide the specified button in the controller bar.
  396. //
  397. // Some explanation is probably useful here: the first thing to understand is that every VR movie has 
  398. // TWO sets of movie controller flags: (1) a set of "control flags" and (2) a set of "explicit flags".
  399. //
  400. // The control flags work as documented in IM: QuickTime Components (pp. 2-20f) and in VRPWQTVR2.0 (pp. 2-23f):
  401. // if a bit in the set of control flags is set (that is, equal to 1), then the associated action or property is
  402. // enabled. For instance, bit 17 (mcFlagQTVRSuppressZoomBtns) means to suppress the zoom buttons. So, if that
  403. // bit is set in a VR movie's control flags, the zoom buttons are NOT displayed. If that bit is clear, the zoom
  404. // buttons are displayed.
  405. //
  406. // However, the QuickTime VR movie controller sometimes suppresses buttons even when those buttons 
  407. // have not been explicitly suppressed in the control flags. For example, if a particular VR movie does not
  408. // contain a sound track, then the movie controller automatically suppresses the speaker/volume button. Likewise,
  409. // if a movie does contain a sound track, then the speaker/volume button is automatically displayed, again without
  410. // regard to the actual value of bit 2 in the control flags.
  411. //
  412. // This might not be what you'd like to happen. For instance, if your application is playing a sound that it
  413. // loaded from a sound resource, you might want the user to be able to adjust the sound's volume using the volume
  414. // control. To do that, you need a way to *force* the speaker/volume button to appear. For this reason, the
  415. // explicit flags were introduced.
  416. //
  417. // The explicit flags indicate which bits in the control flags are to be used explicitly (that is, taken at
  418. // face value). If a certain bit is set in a movie's explicit flags, then the corresponding bit in the control
  419. // flags is interpreted as the desired setting for the feature (and the movie controller will not attempt to
  420. // do anything "clever"). In other words, if bit 17 is set in a movie's explicit flags and bit 17 is clear in
  421. // that movie's control flags, then the zoom buttons are displayed. Similarly, if bit 2 is set in a movie's 
  422. // explicit flags and bit 2 is clear in that movie's control flags, then the speaker/volume button is displayed,
  423. // whether or not the movie contains a sound track.
  424. //
  425. // The final thing to understand: to get or set a bit in a movie's explicit flags, you must set the flag 
  426. // mcFlagQTVRExplicitFlagSet in your call to mcActionGetFlags or mcActionSetFlags. To get or set a bit in a 
  427. // movie's control flags, you must clear the flag mcFlagQTVRExplicitFlagSet in your call to mcActionGetFlags 
  428. // or mcActionSetFlags. Note that when you use the defined constants to set values in the explicit flags, the 
  429. // constant names might be confusing. For instance, setting the bit mcFlagSuppressSpeakerButton in a movie's
  430. // explicit flags doesn't cause the speaker to be suppressed; it just means: "use the actual value of the
  431. // mcFlagSuppressSpeakerButton bit in the control flags".
  432. //
  433. // Whew! Any questions? Okay, then now you'll understand how to hide or show a button in the controller bar:
  434. // set the appropriate explicit flag to 1 and set the corresponding control flag to the desired value. And
  435. // you'll understand how to let the movie controller do its "clever" work: clear the appropriate explicit flag.
  436. //
  437. // There is one final twist to this story: the mcFlag bit for the custom controller button indicates that the
  438. // custom button should be shown, not (like all other button flags) that the button should be suppressed. So
  439. // we need to treat the custom button specially. Sigh.
  440. //
  441. //////////
  442.  
  443. void QTUtils_HideControllerButton (MovieController theMC, long theButton) 
  444. {
  445.     long    myControllerFlags;
  446.     
  447.     // handle the custom button separately
  448.     if (theButton == kQTUtilsCustomButton) {
  449.         MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
  450.         MCDoAction(theMC, mcActionSetFlags, (void *)(myControllerFlags & ~theButton));
  451.     } else {
  452.         // get the current explicit flags and set the explicit flag for the specified button
  453.         myControllerFlags = mcFlagQTVRExplicitFlagSet;
  454.         MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
  455.         MCDoAction(theMC, mcActionSetFlags, (void *)((myControllerFlags | theButton) | mcFlagQTVRExplicitFlagSet));
  456.         
  457.         // get the current control flags and set the suppress flag for the specified button
  458.         myControllerFlags = 0;
  459.         MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
  460.         MCDoAction(theMC, mcActionSetFlags, (void *)((myControllerFlags | theButton) & ~mcFlagQTVRExplicitFlagSet));
  461.     }
  462. }
  463.  
  464.  
  465. //////////
  466. //
  467. // QTUtils_ShowControllerButton
  468. // Show the specified button in the controller bar.
  469. //
  470. //////////
  471.  
  472. void QTUtils_ShowControllerButton (MovieController theMC, long theButton) 
  473. {
  474.     long    myControllerFlags;
  475.     
  476.     // handle the custom button separately
  477.     if (theButton == kQTUtilsCustomButton) {
  478.         MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
  479.         MCDoAction(theMC, mcActionSetFlags, (void *)(myControllerFlags | theButton));
  480.     } else {
  481.         // get the current explicit flags and set the explicit flag for the specified button
  482.         myControllerFlags = mcFlagQTVRExplicitFlagSet;
  483.         MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
  484.         MCDoAction(theMC, mcActionSetFlags, (void *)((myControllerFlags | theButton) | mcFlagQTVRExplicitFlagSet));
  485.         
  486.         // get the current control flags and clear the suppress flag for the specified button
  487.         myControllerFlags = 0;
  488.         MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
  489.         MCDoAction(theMC, mcActionSetFlags, (void *)(myControllerFlags & ~theButton & ~mcFlagQTVRExplicitFlagSet));
  490.     }
  491. }
  492.  
  493.  
  494. //////////
  495. //
  496. // QTUtils_ToggleControllerButton
  497. // Toggle the state of the specified button in the controller bar.
  498. //
  499. //////////
  500.  
  501. void QTUtils_ToggleControllerButton (MovieController theMC, long theButton) 
  502. {
  503.     if (QTUtils_IsControllerButtonVisible(theMC, theButton))    
  504.         QTUtils_HideControllerButton(theMC, theButton);
  505.     else
  506.         QTUtils_ShowControllerButton(theMC, theButton);
  507. }
  508.  
  509.  
  510. //////////
  511. //
  512. // QTUtils_ResetControllerButton
  513. // Remove any explicit setting of the specified button in the controller bar.
  514. // (This allows the QuickTime VR movie controller to be as clever as it knows how to be.)
  515. //
  516. //////////
  517.  
  518. void QTUtils_ResetControllerButton (MovieController theMC, long theButton) 
  519. {
  520.     long    myControllerFlags = mcFlagQTVRExplicitFlagSet;
  521.     
  522.     // get the current explicit flags and clear the explicit flag for the specified button
  523.     MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
  524.     MCDoAction(theMC, mcActionSetFlags, (void *)((myControllerFlags | theButton) & ~mcFlagQTVRExplicitFlagSet));
  525. }
  526.  
  527.  
  528. //////////
  529. //
  530. // QTUtils_IsControllerButtonVisible
  531. // Is the specified button in the controller bar currently visible?
  532. //
  533. //////////
  534.  
  535. Boolean QTUtils_IsControllerButtonVisible (MovieController theMC, long theButton) 
  536. {
  537.     long        myControllerFlags;
  538.  
  539.     // get the current control flags
  540.     myControllerFlags = 0;
  541.     MCDoAction(theMC, mcActionGetFlags, &myControllerFlags);
  542.  
  543.     // the speaker button requires some additional logic, because the QTVR movie controller treats it special;
  544.     // be advised that that controller's special behavior could change in the future, so you might need to tweak this code
  545.     if (theButton == mcFlagSuppressSpeakerButton) {
  546.         long    myExplicitFlags;
  547.         
  548.         // get the current explicit flags
  549.         myExplicitFlags = mcFlagQTVRExplicitFlagSet;
  550.         MCDoAction(theMC, mcActionGetFlags, &myExplicitFlags);
  551.     
  552.         // the speaker button is not showing if the movie has no sound track and the explicit flag is not set
  553.         if (!QTUtils_MovieHasSoundTrack(MCGetMovie(theMC)) && !(myExplicitFlags & theButton))
  554.             return(false);
  555.     }
  556.     
  557.     // the custom button requires some different treatment, since it doesn't have a "Suppress" button constant
  558.     if (theButton == mcFlagsUseCustomButton)
  559.         if (myControllerFlags & theButton)
  560.             return(true);
  561.         else
  562.             return(false);
  563.     
  564.     // examine the suppress flag for the specified button
  565.     if (myControllerFlags & theButton)                // if the button is currently suppressed...
  566.         return(false);
  567.     else
  568.         return(true);
  569. }
  570.  
  571.  
  572. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  573. //
  574. // Media utilities.
  575. //
  576. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  577.  
  578. //////////
  579. //
  580. // QTUtils_IsMediaTypeInMovie
  581. // Determine whether a specific media type is in a movie.
  582. // 
  583. //////////
  584.  
  585. Boolean QTUtils_IsMediaTypeInMovie (Movie theMovie, OSType theMediaType)
  586. {
  587.     return(GetMovieIndTrackType(theMovie, 1, theMediaType, movieTrackMediaType | movieTrackEnabledOnly) != NULL);
  588. }
  589.  
  590.  
  591. //////////
  592. //
  593. // QTUtils_MovieHasTimeCodeTrack
  594. // Determine whether a movie contains a timecode track.
  595. // 
  596. //////////
  597.  
  598. Boolean QTUtils_MovieHasTimeCodeTrack (Movie theMovie)
  599. {
  600.     return(GetMovieIndTrackType(theMovie, 1, TimeCodeMediaType, movieTrackMediaType) != NULL);
  601. }
  602.  
  603.  
  604. //////////
  605. //
  606. // QTUtils_MovieHasSoundTrack
  607. // Determine whether a movie contains a sound track.
  608. // 
  609. //////////
  610.  
  611. Boolean QTUtils_MovieHasSoundTrack (Movie theMovie)
  612. {
  613.     return(GetMovieIndTrackType(theMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly) != NULL);
  614. }
  615.  
  616.  
  617. //////////
  618. //
  619. // QTUtils_GetSoundMediaHandler
  620. // Return the sound media handler for a movie.
  621. // 
  622. //////////
  623.  
  624. MediaHandler QTUtils_GetSoundMediaHandler (Movie theMovie)
  625. {
  626.     Track        myTrack;
  627.     Media        myMedia;
  628.  
  629.     myTrack = GetMovieIndTrackType(theMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
  630.     if (myTrack != NULL) {
  631.         myMedia = GetTrackMedia(myTrack);
  632.         return(GetMediaHandler(myMedia));
  633.     } 
  634.         
  635.     return(NULL);
  636. }
  637.  
  638.  
  639. //////////
  640. //
  641. // QTUtils_UpdateMovieVolumeSetting
  642. // Update the preferred volume setting of the specified movie.
  643. // 
  644. //////////
  645.  
  646. OSErr QTUtils_UpdateMovieVolumeSetting (Movie theMovie)
  647. {
  648.     short            myPrefVolume;
  649.     short            myCurrVolume;
  650.     OSErr            myErr = noErr;
  651.     
  652.     myPrefVolume = GetMoviePreferredVolume(theMovie);
  653.     myCurrVolume = GetMovieVolume(theMovie);
  654.     myCurrVolume = abs(myCurrVolume);
  655.     
  656.     if (myPrefVolume != myCurrVolume) {
  657.         SetMoviePreferredVolume(theMovie, myCurrVolume);
  658.         myErr = GetMoviesError();
  659.     }
  660.     
  661.     return(myErr);
  662. }
  663.  
  664. //////////
  665. //
  666. // QTUtils_MakeSampleDescription
  667. // Return a new image description with default and specified values.
  668. // 
  669. //////////
  670.  
  671. ImageDescriptionHandle QTUtils_MakeSampleDescription (long theEffectType, short theWidth, short theHeight)
  672. {
  673.     ImageDescriptionHandle        mySampleDesc = NULL;
  674.     OSErr                        myErr = noErr;
  675.  
  676.     // create a new sample description
  677.     mySampleDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
  678.     if (mySampleDesc == NULL)
  679.         return(NULL);
  680.     
  681.     // fill in the fields of the sample description
  682.     (**mySampleDesc).idSize = sizeof(ImageDescription);
  683.     (**mySampleDesc).cType = theEffectType;
  684.     (**mySampleDesc).vendor = kAppleManufacturer;
  685.     (**mySampleDesc).temporalQuality = codecNormalQuality;
  686.     (**mySampleDesc).spatialQuality = codecNormalQuality;
  687.     (**mySampleDesc).width = theWidth;
  688.     (**mySampleDesc).height = theHeight;
  689.     (**mySampleDesc).hRes = 72L << 16;
  690.     (**mySampleDesc).vRes = 72L << 16;
  691.     (**mySampleDesc).dataSize = 0L;
  692.     (**mySampleDesc).frameCount = 1;
  693.     (**mySampleDesc).depth = 24;
  694.     (**mySampleDesc).clutID = -1;
  695.     
  696.     return(mySampleDesc);
  697. }
  698.  
  699.  
  700. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  701. //
  702. // User data utilities.
  703. //
  704. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  705.  
  706. #if CONTENT_RATING_AVAIL
  707. //////////
  708. //
  709. // QTUtils_GetContentRatingFromMovie
  710. // Get the content rating from a movie; return an error if it has none. In any case, return a meaningful
  711. // content rating.
  712. //
  713. //////////
  714.  
  715. OSErr QTUtils_GetContentRatingFromMovie (Movie theMovie, UInt16 *theRating, UInt32 *theReasons)
  716. {
  717.     UserData                    myUserData = NULL;
  718.     QTAltContentRatingRecord    myContentRec;
  719.     OSErr                        myErr = paramErr;
  720.  
  721.     *theRating = kQTContentTVYRating;
  722.     *theReasons = 0L;
  723.  
  724.     // make sure we've got a movie
  725.     if (theMovie == NULL)
  726.         return(myErr);
  727.         
  728.     // get the movie's user data list
  729.     myUserData = GetMovieUserData(theMovie);
  730.     if (myUserData != NULL) {
  731.         myErr = GetUserDataItem(myUserData, &myContentRec, sizeof(myContentRec), FOUR_CHAR_CODE('crat'), 0);
  732.         if (myErr == noErr) {
  733.             *theRating = EndianU16_BtoN(myContentRec.ratings);
  734.             *theReasons = EndianU32_BtoN(myContentRec.contentType);
  735.         }
  736.     }
  737.  
  738.     return(myErr);
  739. }
  740.  
  741.  
  742. //////////
  743. //
  744. // QTUtils_AddContentRatingToMovie
  745. // Add a content rating to a movie.
  746. //
  747. // A content rating is stored as a user data item that indicates both the general rating
  748. // (for example, TV-MA [mature audiences only]) and any additional information about the
  749. // content (for example, coarse language). Let's call this additional information the
  750. // "reasons" for the rating. Both the rating and the reasons are specified using constants
  751. // in the file MoviesFormat.h.
  752. //
  753. // This function adds the specified content rating to the movie's user data;
  754. // the updated user data is written to the movie file when the movie is next updated
  755. // (by calling UpdateMovieResource).
  756. // 
  757. //////////
  758.  
  759. OSErr QTUtils_AddContentRatingToMovie (Movie theMovie, UInt16 theRating, UInt32 theReasons)
  760. {
  761.     UserData                    myUserData = NULL;
  762.     QTAltContentRatingRecord    myContentRec;
  763.     OSErr                        myErr = noErr;
  764.  
  765.     // get the movie's user data list
  766.     myUserData = GetMovieUserData(theMovie);
  767.     if (myUserData == NULL)
  768.         return(paramErr);
  769.     
  770.     myContentRec.flags = 0;
  771.     myContentRec.contentType = EndianU32_NtoB(theReasons);
  772.     myContentRec.ratings = EndianU16_NtoB(theRating);
  773.     
  774.     // for simplicity, we assume that we want only one content rating in the movie;
  775.     // as a result, we won't worry about overwriting any existing item of that type
  776.  
  777.     // add the data to the movie's user data
  778.     myErr = SetUserDataItem(myUserData, &myContentRec, sizeof(myContentRec), FOUR_CHAR_CODE('crat'), 0);
  779.  
  780.     return(myErr);
  781. }
  782. #endif    // #if CONTENT_RATING_AVAIL
  783.  
  784.  
  785. //////////
  786. //
  787. // QTUtils_AddUserDataTextToMovie
  788. // Add a user data item, of the specified type, containing the specified text to a movie.
  789. //
  790. // This function adds the specified text to the movie's user data;
  791. // the updated user data is written to the movie file when the movie is next updated
  792. // (by calling UpdateMovieResource).
  793. // 
  794. //////////
  795.  
  796. OSErr QTUtils_AddUserDataTextToMovie (Movie theMovie, char *theText, OSType theType)
  797. {
  798.     UserData                    myUserData = NULL;
  799.     Handle                        myHandle = NULL;
  800.     short                        myIndex = 0;
  801.     long                        myLength = strlen(theText);
  802.     OSErr                        myErr = noErr;
  803.  
  804.     // get the movie's user data list
  805.     myUserData = GetMovieUserData(theMovie);
  806.     if (myUserData == NULL)
  807.         return(paramErr);
  808.     
  809.     // copy the specified text into a new handle
  810.     myHandle = NewHandleClear(myLength);
  811.     if (myHandle == NULL)
  812.         return(MemError());
  813.  
  814.     BlockMoveData(theText, *myHandle, myLength);
  815.  
  816.     // for simplicity, we assume that we want only one user data item of the specified type in the movie;
  817.     // as a result, we won't worry about overwriting any existing item of that type....
  818.     //
  819.     // if you need multiple user data items of a given type (for example, a copyright notice
  820.     // in several different languages), you would need to modify this code; this is left as an exercise
  821.     // for the reader....
  822.  
  823.     // add the data to the movie's user data
  824.     myErr = AddUserDataText(myUserData, myHandle, theType, myIndex + 1, smSystemScript);
  825.  
  826.     // clean up
  827.     DisposeHandle(myHandle);
  828.     return(myErr);
  829. }
  830.  
  831.  
  832. //////////
  833. //
  834. // QTUtils_AddCopyrightToMovie
  835. // Add a user data item containing the specified copyright text to a movie.
  836. //
  837. //////////
  838.  
  839. OSErr QTUtils_AddCopyrightToMovie (Movie theMovie, char *theText)
  840. {
  841.     return(QTUtils_AddUserDataTextToMovie(theMovie, theText, kUserDataTextCopyright));
  842. }
  843.  
  844.  
  845. //////////
  846. //
  847. // QTUtils_AddMovieNameToMovie
  848. // Add a user data item containing the specified name to a movie.
  849. //
  850. //////////
  851.  
  852. OSErr QTUtils_AddMovieNameToMovie (Movie theMovie, char *theText)
  853. {
  854.     return(QTUtils_AddUserDataTextToMovie(theMovie, theText, kUserDataTextFullName));
  855. }
  856.  
  857.  
  858. //////////
  859. //
  860. // QTUtils_AddMovieInfoToMovie
  861. // Add a user data item containing the specified information to a movie.
  862. //
  863. //////////
  864.  
  865. OSErr QTUtils_AddMovieInfoToMovie (Movie theMovie, char *theText)
  866. {
  867.     return(QTUtils_AddUserDataTextToMovie(theMovie, theText, kUserDataTextInformation));
  868. }
  869.  
  870.  
  871. //////////
  872. //
  873. // QTUtils_GetMovieFileLoopingInfo
  874. // Get the looping state of a movie file.
  875. //
  876. // A movie file can have information about its looping state in a user data item of type 'LOOP'.
  877. // If the movie doesn't contain an item of this type, then we'll assume that it isn't looping.
  878. // If it does contain an item of this type, then the item data (a long integer) is 0 for normal
  879. // looping and 1 for palindrome looping. Accordingly, this function returns the following values
  880. // in the theLoopInfo parameter:
  881. //
  882. //        0 == normal looping
  883. //        1 == palindrome looping
  884. //        2 == no looping
  885. //
  886. // Return an error if the movie has no looping state. In any case, return a meaningful looping state.
  887. //
  888. //////////
  889.  
  890. OSErr QTUtils_GetMovieFileLoopingInfo (Movie theMovie, long *theLoopInfo)
  891. {
  892.     UserData        myUserData = NULL;
  893.     long            myLoopInfo = kNoLooping;
  894.     OSErr            myErr = paramErr;
  895.  
  896.     // make sure we've got a movie
  897.     if (theMovie == NULL)
  898.         goto bail;
  899.         
  900.     // get the movie's user data list
  901.     myUserData = GetMovieUserData(theMovie);
  902.     if (myUserData != NULL) {
  903.         myErr = GetUserDataItem(myUserData, &myLoopInfo, sizeof(myLoopInfo), FOUR_CHAR_CODE('LOOP'), 0);
  904.         if (myErr == noErr)
  905.             myLoopInfo = EndianS32_BtoN(myLoopInfo);
  906.     }
  907.  
  908. bail:
  909.     *theLoopInfo = myLoopInfo;
  910.  
  911.     return(myErr);
  912. }
  913.  
  914.  
  915. //////////
  916. //
  917. // QTUtils_SetMovieFileLoopingInfo
  918. // Set the looping state for a movie file.
  919. //
  920. //////////
  921.  
  922. OSErr QTUtils_SetMovieFileLoopingInfo (Movie theMovie, long theLoopInfo)
  923. {
  924.     UserData        myUserData = NULL;
  925.     long            myLoopInfo;
  926.     short            myCount = 0;
  927.     OSErr            myErr = paramErr;
  928.  
  929.     // get the movie's user data
  930.     myUserData = GetMovieUserData(theMovie);
  931.     if (myUserData == NULL)
  932.         goto bail;
  933.  
  934.     // we want to end up with at most one user data item of type 'LOOP',
  935.     // so let's remove any existing ones
  936.     myCount = CountUserDataType(myUserData, FOUR_CHAR_CODE('LOOP'));
  937.     while (myCount--)
  938.         RemoveUserData(myUserData, FOUR_CHAR_CODE('LOOP'), 1);
  939.  
  940.     // make sure we're writing big-endian data
  941.     myLoopInfo = EndianU32_NtoB(theLoopInfo);
  942.     
  943.     switch (theLoopInfo) {
  944.         case kNormalLooping:
  945.         case kPalindromeLooping:
  946.             myErr = SetUserDataItem(myUserData, &myLoopInfo, sizeof(long), FOUR_CHAR_CODE('LOOP'), 0);
  947.             break;
  948.  
  949.         case kNoLooping:
  950.         default:
  951.             myErr = noErr;
  952.             break;
  953.     }
  954.  
  955. bail:
  956.     return(myErr);
  957. }
  958.  
  959.  
  960. //////////
  961. //
  962. // QTUtils_SetLoopingStateFromFile
  963. // Set the looping state for a movie based on the looping information in the movie file.
  964. //
  965. //////////
  966.  
  967. OSErr QTUtils_SetLoopingStateFromFile (Movie theMovie, MovieController theMC)
  968. {
  969.     long             myLoopInfo = kNoLooping;
  970.     OSErr            myErr = noErr;
  971.  
  972.     myErr = QTUtils_GetMovieFileLoopingInfo(theMovie, &myLoopInfo);
  973.     switch (myLoopInfo) {
  974.  
  975.         case kNormalLooping:
  976.             MCDoAction(theMC, mcActionSetLooping, (void *)true);
  977.             MCDoAction(theMC, mcActionSetLoopIsPalindrome, (void *)false);
  978.             break;
  979.  
  980.         case kPalindromeLooping:
  981.             MCDoAction(theMC, mcActionSetLooping, (void *)true);
  982.             MCDoAction(theMC, mcActionSetLoopIsPalindrome, (void *)true);
  983.             break;
  984.  
  985.         case kNoLooping:
  986.         default:
  987.             MCDoAction(theMC, mcActionSetLooping, (void *)false);
  988.             MCDoAction(theMC, mcActionSetLoopIsPalindrome, (void *)false);
  989.             break;
  990.     }
  991.  
  992.     return(myErr);
  993. }
  994.  
  995.  
  996. //////////
  997. //
  998. // QTUtils_MakeMovieLoop
  999. // Set the specified movie to loop.
  1000. //
  1001. // Based on the function MakeMovieLoop by Kevin Marks.
  1002. //
  1003. //////////
  1004.  
  1005. OSErr QTUtils_MakeMovieLoop (Movie theMovie, Boolean isPalindrome)
  1006. {
  1007.     TimeBase        myTimeBase = NULL;
  1008.     long             myFlags = 0L;
  1009.     OSErr            myErr = paramErr;
  1010.  
  1011.     // make sure we've got a movie
  1012.     if (theMovie == NULL)
  1013.         goto bail;
  1014.     
  1015.     myErr = noErr;
  1016.         
  1017.     // set the movie's play hints to enhance looping performance
  1018.     SetMoviePlayHints(theMovie, hintsLoop, hintsLoop);
  1019.     
  1020.     // set the looping flag of the movie's time base
  1021.     myTimeBase = GetMovieTimeBase(theMovie);
  1022.     myFlags = GetTimeBaseFlags(myTimeBase);
  1023.     myFlags |= loopTimeBase;
  1024.     
  1025.     // set or clear the palindrome flag, depending on the specified setting
  1026.     if (isPalindrome)
  1027.         myFlags |= palindromeLoopTimeBase;
  1028.     else
  1029.         myFlags &= ~palindromeLoopTimeBase;
  1030.         
  1031.     SetTimeBaseFlags(myTimeBase, myFlags);
  1032.  
  1033. bail:
  1034.     return(myErr);
  1035. }
  1036.  
  1037.  
  1038. //////////
  1039. //
  1040. // QTUtils_GetWindowPositionFromFile
  1041. // Return, through thePoint, the stored position of the specified movie.
  1042. //
  1043. // Return an error if the movie has no stored position. In any case, return a meaningful position.
  1044. //
  1045. //////////
  1046.  
  1047. OSErr QTUtils_GetWindowPositionFromFile (Movie theMovie, Point *thePoint)
  1048. {
  1049.     UserData        myUserData = NULL;
  1050.     Point            myPoint = {kDefaultWindowX, kDefaultWindowY};
  1051.     OSErr            myErr = paramErr;
  1052.  
  1053.     // make sure we've got a movie
  1054.     if (theMovie == NULL)
  1055.         goto bail;
  1056.         
  1057.     // get the movie's user data list
  1058.     myUserData = GetMovieUserData(theMovie);
  1059.     if (myUserData != NULL) {
  1060.         myErr = GetUserDataItem(myUserData, &myPoint, sizeof(Point), FOUR_CHAR_CODE('WLOC'), 0);
  1061.         if (myErr == noErr) {
  1062.             myPoint.v = EndianS16_BtoN(myPoint.v);
  1063.             myPoint.h = EndianS16_BtoN(myPoint.h);
  1064.         }
  1065.     }
  1066.  
  1067. bail:
  1068.     *thePoint = myPoint;
  1069.  
  1070.     return(myErr);
  1071. }
  1072.  
  1073.  
  1074. //////////
  1075. //
  1076. // QTUtils_GetTrackName
  1077. // Get the name of the specified movie track.
  1078. //
  1079. // This routine is modelled on the one contained in Dispatch 2 from the Ice Floe;
  1080. // I've modified it to return a C string instead of a Pascal string.
  1081. //
  1082. // The caller is responsible for disposing of the pointer returned by this function (by calling free).
  1083. //
  1084. //////////
  1085.  
  1086. char *QTUtils_GetTrackName (Track theTrack)
  1087. {
  1088.     UserData            myUserData;
  1089.     char                *myString = NULL;
  1090.      OSErr                myErr = noErr;
  1091.  
  1092.     // make sure we've got a track
  1093.     if (theTrack == NULL)
  1094.         return(NULL);
  1095.         
  1096.     // a track's name (if it has one) is stored in the track's user data
  1097.     myUserData = GetTrackUserData(theTrack);
  1098.     if (myUserData != NULL) {
  1099.         Handle            myHandle = NewHandle(0);
  1100.  
  1101.         // get the user data item of type kUserDataName;
  1102.         // the handle we pass to GetUserData is resized to contain the track name
  1103.         myErr = GetUserData(myUserData, myHandle, kUserDataName, 1);
  1104.         if (myErr == noErr) {
  1105.             long        myLength = GetHandleSize(myHandle);
  1106.  
  1107.             if (myLength > 0) {
  1108.                 myString = (char *)malloc(myLength + 1);
  1109.                 if (myString != NULL) {
  1110.                     memcpy(myString, *myHandle, myLength);
  1111.                     myString[myLength] = '\0';
  1112.                 }
  1113.             }            
  1114.         }    
  1115.  
  1116.         DisposeHandle(myHandle);
  1117.     }
  1118.  
  1119.     return(myString);
  1120. }
  1121.  
  1122.  
  1123. //////////
  1124. //
  1125. // QTUtils_SetTrackName
  1126. // Set the name of the specified movie track.
  1127. //
  1128. // This function adds the specified text to the track's user data;
  1129. // the updated user data is written to the movie file when the movie is next updated
  1130. // (by calling UpdateMovieResource).
  1131. //
  1132. //////////
  1133.  
  1134. OSErr QTUtils_SetTrackName (Track theTrack, char *theText)
  1135. {
  1136.     UserData            myUserData;
  1137.     OSErr                myErr = noErr;
  1138.  
  1139.     // make sure we've got a track and a name
  1140.     if ((theTrack == NULL) || (theText == NULL))
  1141.         return(paramErr);
  1142.         
  1143.     // get the track's user data list
  1144.     myUserData = GetTrackUserData(theTrack);
  1145.     if (myUserData == NULL)
  1146.         return(paramErr);
  1147.     
  1148.     // remove any existing track name
  1149.     while (RemoveUserData(myUserData, kUserDataName, 1) == noErr)
  1150.         ;
  1151.  
  1152.     myErr = SetUserDataItem(myUserData, theText, strlen(theText), kUserDataName, 0);
  1153.  
  1154.     return(myErr);
  1155. }
  1156.  
  1157.  
  1158. //////////
  1159. //
  1160. // QTUtils_MakeTrackNameByType
  1161. // Create a (unique) name for the specified track, based on the track's type.
  1162. //
  1163. // Given a movie track, this routine constructs a name for that track based on
  1164. // the media type of that track. For instance, if the track is a sound track,
  1165. // this routine returns the name "Sound". However, if there is more than one
  1166. // track of that media type, then this routine numbers the track names. So, if
  1167. // there are two sound tracks, this routine names them "Sound 1" and "Sound 2".
  1168. //
  1169. // This routine is modelled on the one contained in Dispatch 2 from the Ice Floe;
  1170. // I've modified it to return a C string instead of a Pascal string.
  1171. //
  1172. // The caller is responsible for disposing of the pointer returned by this function (by calling free).
  1173. //
  1174. //////////
  1175.  
  1176. char *QTUtils_MakeTrackNameByType (Track theTrack)
  1177. {
  1178.     Media                myMedia;
  1179.     char                *myString = NULL;
  1180.      ComponentResult        myErr = noErr;
  1181.  
  1182.     // make sure we've got a track
  1183.     if (theTrack == NULL)
  1184.         return(NULL);
  1185.     
  1186.     myMedia = GetTrackMedia(theTrack);
  1187.     if (myMedia != NULL) {
  1188.         MediaHandler    myMediaHandler = GetMediaHandler(myMedia);
  1189.         OSType            myMediaType;
  1190.         Str255            myName;
  1191.  
  1192.         // get the media type of the track
  1193.         GetMediaHandlerDescription(myMedia, &myMediaType, NULL, NULL);
  1194.         
  1195.         // get the text of the media type
  1196.         myErr = MediaGetName(myMediaHandler, myName, 0, NULL);
  1197.         if (myErr == noErr) {
  1198.             
  1199.             // determine whether there's more than one track with this media type
  1200.             if (GetMovieIndTrackType(GetTrackMovie(theTrack), 2, myMediaType, movieTrackMediaType) != NULL) {
  1201.  
  1202.                 // add an index number to the track type string we constructed above
  1203.                 long    myIndex = 1;
  1204.                 Str255    myNumString;
  1205.  
  1206.                 while (GetMovieIndTrackType(GetTrackMovie(theTrack), myIndex, myMediaType, movieTrackMediaType) != theTrack)
  1207.                    myIndex++;
  1208.  
  1209.                 NumToString(myIndex, myNumString);
  1210.                 myName[++myName[0]] = ' ';
  1211.                 BlockMoveData(&myNumString[1], &myName[myName[0] + 1], myNumString[0]);
  1212.                 myName[0] += myNumString[0];
  1213.             }
  1214.  
  1215.             // now copy the string data from the Pascal string to a C string
  1216.             if (myName[0] > 0) {
  1217.                 myString = (char *)malloc(myName[0] + 1);
  1218.                 if (myString != NULL) {
  1219.                     memcpy(myString, &myName[1], myName[0]);
  1220.                     myString[myName[0]] = '\0';
  1221.                 }
  1222.             }            
  1223.         }
  1224.     }
  1225.  
  1226.     return(myString);
  1227. }
  1228.  
  1229.  
  1230. //////////
  1231. //
  1232. // QTUtils_IsImageFile
  1233. // Is the specified file an image file?
  1234. //
  1235. //////////
  1236.  
  1237. Boolean QTUtils_IsImageFile (FSSpec *theFSSpec)
  1238. {    
  1239.     GraphicsImportComponent        myImporter = NULL;
  1240.  
  1241.     GetGraphicsImporterForFile(theFSSpec, &myImporter);
  1242.     if (myImporter != NULL)
  1243.         CloseComponent(myImporter);
  1244.  
  1245.     return(myImporter != NULL);
  1246. }
  1247.  
  1248.  
  1249. //////////
  1250. //
  1251. // QTUtils_IsMovieFile
  1252. // Is the specified file a file that can be opened by QuickTime as a movie?
  1253. //
  1254. //////////
  1255.  
  1256. Boolean QTUtils_IsMovieFile (FSSpec *theFSSpec)
  1257. {    
  1258.     Boolean                        isMovieFile = false;
  1259.     AliasHandle                    myAlias = NULL;
  1260.     Component                    myImporter = NULL;
  1261.     FInfo                        myFinderInfo;
  1262.     OSErr                        myErr = noErr;
  1263.             
  1264.     // see whether the file type is MovieFileType; to do this, get the Finder information
  1265.     myErr = FSpGetFInfo(theFSSpec, &myFinderInfo);    
  1266.     if (myErr == noErr)
  1267.         if (myFinderInfo.fdType == kQTFileTypeMovie)
  1268.             return(true);
  1269.  
  1270.     // if it isn't a movie file, see whether the file can be imported as a movie
  1271.     myErr = QTNewAlias(theFSSpec, &myAlias, true);
  1272.     if (myErr == noErr) {
  1273.         if (myAlias != NULL) {
  1274.             myErr = GetMovieImporterForDataRef(rAliasType, (Handle)myAlias, kGetMovieImporterDontConsiderGraphicsImporters, &myImporter);
  1275.             DisposeHandle((Handle)myAlias);
  1276.         }
  1277.     }
  1278.         
  1279.     if ((myErr == noErr) && (myImporter != NULL))        // this file is a movie file
  1280.         isMovieFile = true;
  1281.  
  1282.     return(isMovieFile);
  1283. }
  1284.  
  1285.  
  1286. //////////
  1287. //
  1288. // QTUtils_ConvertFloatToBigEndian
  1289. // Convert the specified floating-point number to big-endian format.
  1290. //
  1291. //////////
  1292.  
  1293. void QTUtils_ConvertFloatToBigEndian (float *theFloat)
  1294. {
  1295.     unsigned long        *myLongPtr;
  1296.     
  1297.     myLongPtr = (unsigned long *)theFloat;
  1298.     *myLongPtr = EndianU32_NtoB(*myLongPtr);
  1299. }
  1300.  
  1301.  
  1302. //////////
  1303. //
  1304. // QTUtils_ConvertCToPascalString
  1305. // Convert a C string into a Pascal string.
  1306. //
  1307. // The caller is responsible for disposing of the pointer returned by this function (by calling free).
  1308. //
  1309. //////////
  1310.  
  1311. StringPtr QTUtils_ConvertCToPascalString (char *theString)
  1312. {
  1313.     StringPtr    myString = (StringPtr )malloc(strlen(theString) + 1);
  1314.     short        myIndex = 0;
  1315.  
  1316.     while (theString[myIndex] != '\0') {
  1317.         myString[myIndex + 1] = theString[myIndex];
  1318.         myIndex++;
  1319.     }
  1320.     
  1321.     myString[0] = (unsigned char)myIndex;
  1322.     
  1323.     return(myString);
  1324. }
  1325.  
  1326.  
  1327. //////////
  1328. //
  1329. // QTUtils_ConvertPascalToCString
  1330. // Convert a Pascal string into a C string.
  1331. //
  1332. // The caller is responsible for disposing of the pointer returned by this function (by calling free).
  1333. //
  1334. //////////
  1335.  
  1336. char *QTUtils_ConvertPascalToCString (StringPtr theString)
  1337. {
  1338.     char         *myString = (char *)malloc(theString[0] + 1);
  1339.     short        myIndex = 0;
  1340.  
  1341.     for (myIndex = 0; myIndex < theString[0]; myIndex++)
  1342.         myString[myIndex] = theString[myIndex + 1];
  1343.     
  1344.     myString[theString[0]] = '\0';
  1345.     
  1346.     return(myString);
  1347. }
  1348.  
  1349.  
  1350. //////////
  1351. //
  1352. // QTUtils_DeleteAllReferencesToTrack
  1353. // Delete all existing track references to the specified track.
  1354. //
  1355. //////////
  1356.  
  1357. OSErr QTUtils_DeleteAllReferencesToTrack (Track theTrack)
  1358. {
  1359.     Movie                myMovie = NULL;
  1360.     Track                myTrack = NULL;
  1361.     long                myTrackCount = 0L;
  1362.     long                myTrRefCount = 0L;
  1363.     long                myTrackIndex;
  1364.     long                myTrRefIndex;
  1365.     OSErr                myErr = noErr;
  1366.  
  1367.     myMovie = GetTrackMovie(theTrack);
  1368.     if (myMovie == NULL)
  1369.         return(paramErr);
  1370.  
  1371.     // iterate thru all the tracks in the movie (that are different from the specified track)
  1372.     myTrackCount = GetMovieTrackCount(myMovie);
  1373.     for (myTrackIndex = 1; myTrackIndex <= myTrackCount; myTrackIndex++) {
  1374.         myTrack = GetMovieIndTrack(myMovie, myTrackIndex);
  1375.         if ((myTrack != NULL) && (myTrack != theTrack)) {
  1376.             OSType        myType = 0L;
  1377.     
  1378.             // iterate thru all track reference types contained in the current track
  1379.             myType = GetNextTrackReferenceType(myTrack, myType);
  1380.             while (myType != 0L) {
  1381.  
  1382.                 // iterate thru all track references of the current type;
  1383.                 // note that we count down to 1, since DeleteTrackReference will cause
  1384.                 // any higher-indexed track references to be renumbered
  1385.                 myTrRefCount = GetTrackReferenceCount(myTrack, myType);
  1386.                 for (myTrRefIndex = myTrRefCount; myTrRefIndex >= 1; myTrRefIndex--) {
  1387.                     Track    myRefTrack = NULL;
  1388.  
  1389.                     myRefTrack = GetTrackReference(myTrack, myType, myTrRefIndex);
  1390.                     if (myRefTrack == theTrack)
  1391.                         myErr = DeleteTrackReference(myTrack, myType, myTrRefIndex);
  1392.                 }
  1393.  
  1394.                 myType = GetNextTrackReferenceType(myTrack, myType);
  1395.             }
  1396.         }
  1397.     }
  1398.  
  1399.     return(myErr);
  1400. }
  1401.  
  1402.  
  1403. //////////
  1404. //
  1405. // QTUtils_GetFrameDuration
  1406. // Get the duration of the first sample frame in the specified movie track.
  1407. //
  1408. //////////
  1409.  
  1410. TimeValue QTUtils_GetFrameDuration (Track theTrack)
  1411. {    
  1412.     TimeValue    mySampleDuration;
  1413.     OSErr        myErr = noErr;
  1414.  
  1415.     myErr = GetMediaSample(    GetTrackMedia(theTrack),
  1416.                             NULL,         // don't return sample data
  1417.                             0,
  1418.                             NULL,        // don't return number of bytes of sample data returned
  1419.                             0,
  1420.                             NULL,
  1421.                             &mySampleDuration,
  1422.                             NULL,        // don't return sample description
  1423.                             NULL,        // don't return sample description index
  1424.                             0,            // no max number of samples
  1425.                             NULL,        // don't return number of samples returned
  1426.                             NULL);        // don't return sample flags
  1427.  
  1428.     // make sure we return a legitimate value even if GetMediaSample encounters an error
  1429.     if (myErr != noErr)
  1430.         mySampleDuration = 0;
  1431.  
  1432.     return(mySampleDuration);
  1433. }
  1434.  
  1435.  
  1436. //////////
  1437. //
  1438. // QTUtils_GetFrameCount
  1439. // Get the number of frames in the specified movie track. We return the value -1 if
  1440. // an error occurs and we cannot determine the number of frames in the track.
  1441. //
  1442. // Based (loosely) on frame-counting code in ConvertToMovie Jr.c.
  1443. // 
  1444. // We count the frames in the track by stepping through all of its interesting times
  1445. // (the places where the track displays a new sample).
  1446. //
  1447. //////////
  1448.  
  1449. long QTUtils_GetFrameCount (Track theTrack)
  1450. {    
  1451.     long        myCount = -1;
  1452.     short        myFlags;
  1453.     TimeValue    myTime = 0;
  1454.     
  1455.     if (theTrack == NULL)
  1456.         goto bail;
  1457.         
  1458.     // we want to begin with the first frame (sample) in the track
  1459.     myFlags = nextTimeMediaSample + nextTimeEdgeOK;
  1460.  
  1461.     while (myTime >= 0) {
  1462.         myCount++;
  1463.         
  1464.         // look for the next frame in the track; when there are no more frames,
  1465.         // myTime is set to -1, so we'll exit the while loop
  1466.         GetTrackNextInterestingTime(theTrack, myFlags, myTime, fixed1, &myTime, NULL);
  1467.         
  1468.         // after the first interesting time, don't include the time we're currently at
  1469.         myFlags = nextTimeStep;
  1470.     }
  1471.  
  1472. bail:
  1473.     return(myCount);
  1474. }
  1475.  
  1476.  
  1477. //////////
  1478. //
  1479. // QTUtils_GetMaxWindowDepth
  1480. // Get the deepest pixel type and size of the screen area intersected by a specified window.
  1481. //
  1482. //////////
  1483.  
  1484. #if TARGET_OS_MAC
  1485. void QTUtils_GetMaxWindowDepth (CWindowPtr theWindow, short *thePixelType, short *thePixelSize)
  1486. {
  1487.     Rect            myRect;
  1488.     Point            myPoint = {0, 0};
  1489.     
  1490.     // initialize returned values 
  1491.     *thePixelType = k1MonochromePixelFormat;
  1492.     *thePixelSize = 0;
  1493.  
  1494.     if (theWindow == NULL)
  1495.         return;
  1496.  
  1497.     GetWindowPortBounds(theWindow, &myRect);
  1498.  
  1499.     // assume the window is the current port
  1500.     LocalToGlobal(&myPoint);
  1501.  
  1502.     // offset the rectangle to global coordinates
  1503.     MacOffsetRect(&myRect, myPoint.h, myPoint.v);
  1504.     
  1505.     // get the max data for the global rectangle
  1506.     QTUtils_GetMaxScreenDepth(&myRect, thePixelType, thePixelSize);
  1507. }
  1508. #endif
  1509.  
  1510. //////////
  1511. //
  1512. // QTUtils_GetMaxScreenDepth
  1513. // Get the deepest pixel type and size of the screen area intersected by a specified rectangle.
  1514. //
  1515. //////////
  1516.  
  1517. void QTUtils_GetMaxScreenDepth (Rect *theGlobalRect, short *thePixelType, short *thePixelSize)
  1518. {
  1519.     GDHandle        myGDevice = NULL;
  1520.     PixMapHandle    myPixMap = NULL;
  1521.  
  1522.     myGDevice = GetMaxDevice(theGlobalRect);    // get the max device
  1523.     if (myGDevice != NULL) {
  1524.         // get the pixmap for the max device
  1525.         myPixMap = (**myGDevice).gdPMap;
  1526.         if (myPixMap != NULL) {
  1527.             // extract the interesting info from the pixmap of the device
  1528.             *thePixelType = (**myPixMap).pixelType;
  1529.             *thePixelSize = (**myPixMap).pixelSize;
  1530.         }
  1531.     }
  1532. }
  1533.  
  1534.  
  1535. //////////
  1536. //
  1537. // QTUtils_GetUsersConnectionSpeed
  1538. // Return the connection speed selected by the user in the QuickTime Settings control panel;
  1539. // return 0 if the user's QuickTime preferences cannot be read.
  1540. //
  1541. // Based on code in Ice Floe Dispatch 17 by Mike Dodd.
  1542. //
  1543. //////////
  1544.  
  1545. long QTUtils_GetUsersConnectionSpeed (void)
  1546. {
  1547.     QTAtomContainer                    myPrefsContainer = NULL;
  1548.     QTAtom                            myPrefsAtom = 0;
  1549.     ConnectionSpeedPrefsRecord        myPrefsRec;
  1550.     long                            myDataSize = 0L;
  1551.     long                            mySpeed = 0L;
  1552.     Ptr                                myAtomData = NULL;
  1553.     OSErr                            myErr = noErr;
  1554.  
  1555.     // you can retrieve the user's QuickTime preferences by calling GetQuickTimePreference;
  1556.     // the first parameter indicates the type of preference you want information about, and
  1557.     // the second parameter is an atom container that contains the returned preference data 
  1558.     myErr = GetQuickTimePreference(ConnectionSpeedPrefsType, &myPrefsContainer);
  1559.     if (myErr == noErr) {
  1560.             
  1561.         // find the atom of the desired type
  1562.         myPrefsAtom = QTFindChildByID(myPrefsContainer, kParentAtomIsContainer, ConnectionSpeedPrefsType, 1, NULL);
  1563.         if (myPrefsAtom == 0) {
  1564.             // we did not find any such atom in the returned atom container, so we'll
  1565.             // return a default connection speed setting of 28.8K bytes per second
  1566.             mySpeed = kDataRate288ModemRate;
  1567.         } else {
  1568.             // we found the desired atom in the returned atom container;
  1569.             // read the data contained in that atom and verify that the data is of the
  1570.             // size we are expecting
  1571.             QTGetAtomDataPtr(myPrefsContainer, myPrefsAtom, &myDataSize, &myAtomData);
  1572.             
  1573.             if (myDataSize != sizeof(ConnectionSpeedPrefsRecord)) {
  1574.                 // the data in the atom isn't the right size, so it must be corrupt;
  1575.                 // return a default connection speed setting of 28.8K bytes per second
  1576.                 mySpeed = kDataRate288ModemRate;
  1577.             } else {
  1578.                 // everything is fine: read the connection speed
  1579.                 
  1580.                 // NOTE: the data in this atom is native-endian, so we do not need to
  1581.                 // perform any endian-swapping when extracting the speed from the atom.
  1582.                 // (This is an exception to the rule that data in atom containers is
  1583.                 // always big-endian.)
  1584.                 myPrefsRec = *(ConnectionSpeedPrefsRecord *)myAtomData;
  1585.                 mySpeed = myPrefsRec.connectionSpeed;
  1586.             }
  1587.         }
  1588.  
  1589.         QTDisposeAtomContainer(myPrefsContainer);
  1590.     }
  1591.     
  1592.     return(mySpeed);
  1593. }
  1594.  
  1595.  
  1596. //////////
  1597. //
  1598. // QTUtils_SetUsersConnectionSpeed
  1599. // Set the connection speed in the QuickTime Settings control panel to the specified value.
  1600. //
  1601. // NOTE: In general, you should let the user decide the connection speed (using the QuickTime
  1602. // Settings control panel). In some cases, however, you might need to do this programmatically.
  1603. // Also, you should in general use values for theSpeed that are enumerated in the header file
  1604. // MoviesFormat.h.
  1605. //
  1606. // Based on code in Ice Floe Dispatch 17 by Mike Dodd.
  1607. //
  1608. //////////
  1609.  
  1610. OSErr QTUtils_SetUsersConnectionSpeed (long theSpeed)
  1611. {
  1612.     QTAtomContainer                    myPrefsContainer = NULL;
  1613.     QTAtom                            myPrefsAtom = 0;
  1614.     ConnectionSpeedPrefsRecord        myPrefsRec;
  1615.     OSErr                            myErr = noErr;
  1616.  
  1617.     myErr = QTNewAtomContainer(&myPrefsContainer);
  1618.     if (myErr == noErr) {
  1619.         // NOTE: the data in this atom is native-endian, so we do not need to
  1620.         // perform any endian-swapping when inserting the speed into the atom.
  1621.         // (This is an exception to the rule that data in atom containers is
  1622.         // always big-endian.)
  1623.         myPrefsRec.connectionSpeed = theSpeed;
  1624.         
  1625.         myErr = QTInsertChild(myPrefsContainer, kParentAtomIsContainer, ConnectionSpeedPrefsType, 1, 0, sizeof(ConnectionSpeedPrefsRecord), &myPrefsRec, &myPrefsAtom);
  1626.         if (myErr == noErr)
  1627.             myErr = SetQuickTimePreference(ConnectionSpeedPrefsType, myPrefsContainer);
  1628.             
  1629.         QTDisposeAtomContainer(myPrefsContainer);
  1630.     }
  1631.  
  1632.     return(myErr);
  1633. }
  1634.  
  1635.  
  1636. #if CONTENT_RATING_AVAIL
  1637. //////////
  1638. //
  1639. // QTUtils_GetUsersContentRating
  1640. // Return, through the function's parameters, the content rating and acceptable content types
  1641. // selected by the user in the QuickTime Settings control panel; return an error if the user's
  1642. // QuickTime preferences cannot be read.
  1643. //
  1644. // Based on QTUtils_GetUsersConnectionSpeed.
  1645. //
  1646. //////////
  1647.  
  1648. OSErr QTUtils_GetUsersContentRating (UInt32 *theType, UInt16 *theRating)
  1649. {
  1650.     QTAtomContainer                    myPrefsContainer = NULL;
  1651.     QTAtom                            myPrefsAtom = 0;
  1652.     ContentRatingPrefsRecord        myContentRec;
  1653.     long                            myDataSize = 0L;
  1654.     Ptr                                myAtomData = NULL;
  1655.     OSErr                            myErr = noErr;
  1656.  
  1657.     // you can retrieve the user's QuickTime preferences by calling GetQuickTimePreference;
  1658.     // the first parameter indicates the type of preference you want information about, and
  1659.     // the second parameter is an atom container that contains the returned preference data 
  1660.     myErr = GetQuickTimePreference(kContentRatingPrefsType, &myPrefsContainer);
  1661.     if (myErr == noErr) {
  1662.             
  1663.         // find the atom of the desired type
  1664.         myPrefsAtom = QTFindChildByID(myPrefsContainer, kParentAtomIsContainer, kContentRatingPrefsType, 1, NULL);
  1665.         if (myPrefsAtom == 0) {
  1666.             // we did not find any such atom in the returned atom container, so we'll
  1667.             // return default settings
  1668.             *theType = 0L;
  1669.             *theRating = kQTContentTVYRating;
  1670.         } else {
  1671.             // we found the desired atom in the returned atom container;
  1672.             // read the data contained in that atom and verify that the data is of the
  1673.             // size we are expecting
  1674.             myErr = QTGetAtomDataPtr(myPrefsContainer, myPrefsAtom, &myDataSize, &myAtomData);
  1675.             
  1676.             if (myDataSize != sizeof(ContentRatingPrefsRecord)) {
  1677.                 // the data in the atom isn't the right size, so it must be corrupt;
  1678.                 // return default settings
  1679.                 *theType = 0L;
  1680.                 *theRating = kQTContentTVYRating;
  1681.             } else {
  1682.                 // everything is fine: read the content information
  1683.                 
  1684.                 // NOTE: the data in this atom is native-endian, so we do not need to
  1685.                 // perform any endian-swapping when extracting the data from the atom.
  1686.                 // (This is an exception to the rule that data in atom containers is
  1687.                 // always big-endian.)
  1688.                 
  1689.                 // WARNING: the format of the data in a content rating atom is, to my
  1690.                 // knowledge, currently undocumented; the following method of extracting
  1691.                 // that info is based on empirical investigation.
  1692.                 myContentRec = *(ContentRatingPrefsRecord *)myAtomData;
  1693.                 *theType = (UInt32)(~(myContentRec.fContentTypes) & 0x00ff);
  1694.                 *theRating = myContentRec.fContentRating;
  1695.             }
  1696.         }
  1697.  
  1698.         QTDisposeAtomContainer(myPrefsContainer);
  1699.     }
  1700.     
  1701.     return(myErr);
  1702. }
  1703. #endif    // #if CONTENT_RATING_AVAIL
  1704.  
  1705.  
  1706. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  1707. //
  1708. // Controller utilities.
  1709. //
  1710. // Use these functions to manipulate movie controllers.
  1711. //
  1712. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  1713.  
  1714. //////////
  1715. //
  1716. // QTUtils_GetControllerType
  1717. // Get the controller type of the specified movie.
  1718. //
  1719. //////////
  1720.  
  1721. OSType QTUtils_GetControllerType (Movie theMovie) 
  1722. {
  1723.     UserData        myUserData = NULL;
  1724.     OSType            myType = kUnknownType;
  1725.     OSErr            myErr = noErr;
  1726.     
  1727.     // make sure we've got a movie
  1728.     if (theMovie == NULL)
  1729.         return(myType);
  1730.         
  1731.     myUserData = GetMovieUserData(theMovie);
  1732.     if (myUserData != NULL) {
  1733.         myErr = GetUserDataItem(myUserData, &myType, sizeof(myType), kUserDataMovieControllerType, 0);
  1734.         if (myErr == noErr)
  1735.             myType = EndianU32_BtoN(myType);
  1736.     }
  1737.     
  1738.     return(myType);
  1739. }
  1740.  
  1741.  
  1742. //////////
  1743. //
  1744. // QTUtils_SetControllerType
  1745. // Set the controller type of the specified movie.
  1746. //
  1747. // This function adds an item to the movie's user data;
  1748. // the updated user data is written to the movie file when the movie is next updated
  1749. // (by calling AddMovieResource or UpdateMovieResource).
  1750. //
  1751. // NOTE: This function is intended to set the controller type of a movie you're building;
  1752. // to change the controller of an open movie, use QTUtils_ChangeControllerType.
  1753. //
  1754. //////////
  1755.  
  1756. OSErr QTUtils_SetControllerType (Movie theMovie, OSType theType)
  1757. {
  1758.     UserData        myUserData;
  1759.     OSErr            myErr = noErr;
  1760.  
  1761.     // make sure we've got a movie
  1762.     if (theMovie == NULL)
  1763.         return(paramErr);
  1764.         
  1765.     // get the movie's user data list
  1766.     myUserData = GetMovieUserData(theMovie);
  1767.     if (myUserData == NULL)
  1768.         return(paramErr);
  1769.     
  1770.     theType = EndianU32_NtoB(theType);
  1771.     myErr = SetUserDataItem(myUserData, &theType, sizeof(theType), kUserDataMovieControllerType, 0);
  1772.  
  1773.     return(myErr);
  1774. }
  1775.  
  1776.  
  1777. //////////
  1778. //
  1779. // QTUtils_ChangeControllerType
  1780. // Change the controller type of the movie that uses the specified controller, "on the fly",
  1781. // and return the new movie controller to the caller; if for some reason we cannot create a
  1782. // new movie controller, return NULL.
  1783. //
  1784. //////////
  1785.  
  1786. MovieController QTUtils_ChangeControllerType (MovieController theMC, OSType theType, long theFlags)
  1787. {
  1788.     MovieController        myMC = NULL;
  1789.     Movie                myMovie = NULL;
  1790.     Rect                myRect;
  1791.     OSErr                myErr = noErr;
  1792.  
  1793.     // make sure we've got a movie controller
  1794.     if (theMC == NULL)
  1795.         return(NULL);
  1796.     
  1797.     // get the movie associated with that controller
  1798.     myMovie = MCGetMovie(theMC);
  1799.     if (myMovie == NULL)
  1800.         return(NULL);
  1801.         
  1802.     GetMovieBox(myMovie, &myRect);
  1803.  
  1804.     // set the new controller type in the movie's user data list
  1805.     myErr = QTUtils_SetControllerType(myMovie, theType);
  1806.     if (myErr != noErr)
  1807.         return(NULL);
  1808.  
  1809.     // dispose of the existing controller
  1810.     DisposeMovieController(theMC);
  1811.     
  1812.     // create a new controller of the specified type
  1813.     myMC = NewMovieController(myMovie, &myRect, theFlags);
  1814.  
  1815.     return(myMC);
  1816. }
  1817.  
  1818.  
  1819. #endif    // ifndef __QTUtilities__